package online.flowerinsnow.minecraft.bukkit.manhunt;

import online.flowerinsnow.minecraft.bukkit.manhunt.command.*;
import online.flowerinsnow.minecraft.bukkit.manhunt.config.ManhuntConfiguration;
import online.flowerinsnow.minecraft.bukkit.manhunt.core.PlayerTeam;
import online.flowerinsnow.minecraft.bukkit.manhunt.listener.*;
import online.flowerinsnow.minecraft.bukkit.manhunt.manager.*;
import online.flowerinsnow.minecraft.bukkit.manhunt.manager.impl.*;
import online.flowerinsnow.minecraft.bukkit.manhunt.runnable.CompassRunnable;
import online.flowerinsnow.minecraft.bukkit.manhunt.session.player.ActionBarSession;
import org.bukkit.*;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Boat;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;

import java.util.HashSet;
import java.util.Random;

public class Manhunt extends JavaPlugin {
    private static Manhunt instance;
    private static NamespacedKey namespacedKey;

    private static ICompassManager compassManager;
    private static ITeamManager teamManager;
    private static IWorldManager worldManager;
    private static IBossBarManager bossBarManager;
    private static IScoreboardManager scoreboardManager;

    public static final HashSet<Player> specPlayer = new HashSet<>();
    public static final Random RAND = new Random();
    @Override
    public void onEnable() {
        instance = this;
        namespacedKey = new NamespacedKey(this, getDescription().getName());

        saveDefaultConfig();
        ManhuntConfiguration.load();
        teamManager = new TeamManagerImpl();
        compassManager = new CompassManagerImpl();
        worldManager = new WorldManagerImpl();
        bossBarManager = new BossBarManagerImpl();
        scoreboardManager = new ScoreboardManagerImpl();

        registerListeners(new LobbySpectatorListener(), new PlayerListener(), new StateListener(), new DamageListener(), new SkullEatListener(), new InvisibleListener(), new FastMineListener(), new SpecialMonsterListener());

        registerCommands("manhunt", new CommandManhunt());
        registerCommands("flowerairadaranticheat", new CommandFlowerAIRadarAntiCheat());
        registerCommands("teamchat", new CommandTeamChat());
        registerCommands("teamchatcoords", new CommandTeamChatCoords());
        registerCommands("ping", new CommandPing());
        registerCommands("suicide", new CommandSuicide());

        RecipeManager.register();

        new CompassRunnable().start();
        startCheckSpectator();
    }

    @Override
    public void onDisable() {
        getServer().getScheduler().cancelTasks(this);
        InvisibleListener.INVISIBLE_PLAYERS.clear();

        SpecialMonsterListener.clearMonsters();

        Bukkit.removeRecipe(namespacedKey);
        namespacedKey = null;
        instance = null;
    }

    public static Manhunt getInstance() {
        return instance;
    }

    public static ICompassManager getCompassManager() {
        return compassManager;
    }

    public static ITeamManager getTeamManager() {
        return teamManager;
    }

    public static IWorldManager getWorldManager() {
        return worldManager;
    }

    public static IBossBarManager getBossBarManager() {
        return bossBarManager;
    }

    public static IScoreboardManager getScoreboardManager() {
        return scoreboardManager;
    }

    private void registerListeners(Listener... listeners) {
        for (Listener listener : listeners) {
            getServer().getPluginManager().registerEvents(listener, this);
        }
    }
    private void registerCommands(String command, TabExecutor executor) {
        PluginCommand cmd = getCommand(command);
        if (cmd == null) {
            getLogger().warning(command + " 未被注册 跳过");
            return;
        }
        cmd.setExecutor(executor);
        cmd.setTabCompleter(executor);
    }

    public static BukkitTask runTask(Runnable runnable) {
        return Bukkit.getScheduler().runTask(getInstance(), runnable);
    }

    private void startCheckSpectator() {
        getServer().getScheduler().runTaskTimer(this, this::checkOnce, 0L, 20L);
    }

    private void checkOnce() {
        Bukkit.getOnlinePlayers().forEach(this::checkPlayer);
    }

    private void checkPlayer(Player player) {
        PlayerTeam team = Manhunt.getTeamManager().getPlayerTeam(player);
        if (team != PlayerTeam.SPECTATOR) return;
        Location location = player.getLocation();

        World world = location.getWorld();
        ActionBarSession acb = new ActionBarSession(player);
        //noinspection ConstantConditions
        if (world.getEntities().stream().anyMatch(e -> {
            if (e.getLocation().distance(player.getLocation()) > 5.0) return false;
            if (e instanceof ExperienceOrb || e instanceof Boat || e instanceof ItemFrame) {
                return true;
            }
            if (e instanceof Player) {
                Player target = (Player) e;
                PlayerTeam tt = Manhunt.getTeamManager().getPlayerTeam(target);
                return tt != null && tt != PlayerTeam.SPECTATOR;
            }
            return false;
        })) {
            if (specPlayer.add(player)) {
                player.setGameMode(GameMode.SPECTATOR);
                acb.sendActionBar("&7[&b!&7]&b您距离可交互实体较近，已为您转换至旁观模式。");
                Bukkit.getScheduler().runTaskLater(Manhunt.getInstance(), () ->
                        acb.sendActionBar("&7[&b!&7]&b若要离开这个模式，请确保周围没有可交互实体并且没有卡在方块里。"),
                        40L);
            }
        } else if (specPlayer.contains(player)) {
            if (player.getLocation().getBlock().getType().isAir() && player.getEyeLocation().getBlock().getType().isAir()) {
                player.setGameMode(GameMode.SURVIVAL);
                player.setAllowFlight(true);
                player.setFlying(true);
                acb.sendActionBar("&7[&b!&7]&b您已离开旁观模式。");
                specPlayer.remove(player);
            }
        }
    }

    public static NamespacedKey getNamespacedKey() {
        return namespacedKey;
    }
}
